import React, {Component} from 'react';
import Menu from "./menu";
import Navbar from "./navbar";
import Table from "./table";
import TableForSequential from "./table";
import $ from "jquery";

let reset = false;

const getInput = (id) => {
        let temp = document.getElementById(id).value;
        return temp.split(",").map((value) => parseInt(value));

}


class Search extends Component {
    constructor() {
        super();
        this.state = {
            algo:0,
            reset: false,
            key: -1,
            array: [],
            start: false,
            allArray:[],
            tables:[],
            // for hash search
            finalTable: null,
            hashTables: [],
            text:[]

        }
        this.primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41];
    }



    handleGenerate = async () => {
        let inputElement = $("#array");
        await this.realOnReset();
        const randomList = (n=undefined, range=undefined) => {
                let dict = {};
                let count = 0;
                let randomN;
                let randomRange = [...range];

                randomN = 0 | (1 + Math.random() * n);
                console.log("randomN", randomN)
                while (count < randomN) {
                    let random = 0 | (Math.random() * (randomRange[1] - randomRange[0] + 1) +  randomRange[0]);
                    if (!dict[random]) {
                        dict[random] = 1;
                        count ++;
                    }
                }
                let list = []
                for (let key in dict) {
                    list.push(key);
                }
                let length = list.length;
                const final = [];
                while (list.length !== 0) {
                    final.push(list.splice(Math.floor(Math.random() * list.length), 1)[0]);
                }
                return [randomN, final];
            }
            let [_, list] = randomList(20, [1,100]);
            inputElement.val(list.reduce((a, b) => {return a + "," + b;}));

            inputElement = $("#key");
            let random_1 = Math.floor(Math.random() * 100);
            if (random_1 < 50) {
                let random_2 = Math.floor(Math.random() * list.length);
                console.log("index", random_2);
                console.log("array", list);
                inputElement.val("" + list[random_2]);
            } else {
                inputElement.val("" + Math.floor(Math.random() * 100));
            }
    }

    realOnReset = async () => {
        this.onReset();
    }

    onReset = () => {
        this.setState({reset: true,
            key: -1,
            array: [],
            start: false,
            allArray:[],
            tables:[],
            finalTable: null,
            hashTables: [],
            text:[]
        });
        reset = true;
    }

    handleInput = async () => {
        try {
            let [key] = getInput("key");
            let array = getInput("array");
            if (this.state.algo === 1) {
                array = array.sort((a,b)=>a-b);
            }
            let map = new Map();
            for (let i in array) {
                map.set(array[i], 1);
            }
            array = [];
            for (let [key, _] of map) {
                array.push(key);
            }
            document.getElementById("array").value = array.reduce((a,b)=>a + ',' + b);
            this.setState({key, array});
        }
         catch (e) {

        }

    }

    setAlgo = async (pos, val) => {
        if (pos === 0) {
            await this.realOnReset();
            this.setState({algo: val});
        }
    }

    addNumber = async ()=>{
        await this.onReset();
        await this.handleInput();
        await this.addNumberTemp();

    }

    addNumberTemp = async () => {
        this.setState({reset: false});
        this.setState({start: false});
        reset = false;

        if (this.state.algo === 0) {
            this.sequentialSearch();
        } else if (this.state.algo === 1) {
            this.binarySearch()
        } else {
            this.hashSearch();
        }

    }

    generateArray = (start, end, array) => {
        let list = []
        for (let i = start; i <= end; i++) {
            list.push(array[i]);
        }
        return list;
    }

    sequentialSearch = () => {
        let allArray = [];
        let key = this.state.key;
        let array = [...this.state.array];
        allArray.push(array);
        let length = array.length;
        let index = -1;
        let tables = [];
        let listN = [];
        let listRes = [...array];
        array.map((x,i)=>listN.push(i));
        for (let i = 0; i < array.length; i++) {
            let temp = [];
            temp.push(`LOOP: ${i}`);

            let clue = `Array[${i}]=${array[i]}`;
            if (array[i] === key)
                clue += " is equal to ";
            else
                clue += " is not equal to ";
            clue += key;
            temp.push(clue);

            // temp.push([0, length - 1]);

            allArray.push(temp);
            tables.push(
                <TableForSequential
                listN={listN}
                nowIndexRes={i}
                listRes={listRes}
                flag = {key === array[i]}
                />
            )
            if (key === array[i]) {
                index = i;
                break;
            }
        }
        if (index !== -1) {
            allArray.splice(0,0,"Found: index is " + index);
        } else {
            allArray.splice(0, 0, "Not Found!");
        }
        console.log(tables);
        this.setState({allArray, tables, start: true });

    }

    binarySearch = () => {
        let allArray = [];
        let key = this.state.key;
        let array = [...this.state.array];
        allArray.push(array);
        let length = array.length;
        let index = -1;
        let tables = [];
        let listN = [];
        let listRes = [...array];
        array.map((x,i)=>listN.push(i));
        let left = 0;
        let right = length - 1;

        for (let i = 0; left <= right; i++) {
            let mid = parseInt((left + right) / 2);
            let temp = [];
            temp.push(`LOOP: ${i}`);
            let clue = `leftIndex: ${left}, rightIndex: ${right}, midIndex: ${mid}, Array[${mid}]=${array[mid]}`;
            if (array[mid] === key)
                clue += " is equal to ";
            else
                clue += " is not equal to ";
            clue += key;
            temp.push(clue);
            allArray.push(temp);
            tables.push(
                <TableForSequential
                listN={this.generateArray(left, right, listN)}
                nowIndexRes={mid - left}
                listRes={this.generateArray(left, right, listRes)}
                flag = {key === array[mid]}
                />
            )
            if (key === array[mid]) {
                index = mid;
                break;
            } else if (key > array[mid]) {
                left = mid + 1;
            } else if (key < array[mid]) {
                right = mid - 1;
            }
        }
        if (index !== -1) {
            allArray.splice(0,0,"Found: index is " + index);
        } else {
            allArray.splice(0, 0, "Not Found!");
        }
        this.setState({allArray, tables, start: true });
    }

    hashSearch = () => {
        let allArray = [];
        let key = this.state.key;
        let array = [...this.state.array];
        allArray.push(array);
        let length = array.length;
        for (let i = 0; i < this.primes.length; i++) {
            if (length <= this.primes[i]) {
                length = this.primes[i];
                break;
            }
        }

        let tables = [];
        let listN = [];
        let listRes = [];
        for (let i = 0; i < length; i++) {
            listN.push(i);
            listRes.push("");
        }
        for (let i = 0; i < array.length; i++) {
            let temp = [];
            let listRes2 = [...listRes];
            temp.push(`Insert value: ${array[i]}`);
            allArray.push(temp);
            let mod = array[i] % length;
            let p = mod;
            let loop = 0;
            let temp_clue_table = [];
            while (listRes2[p] !== "") {
                let clue = `Loop: ${loop++}`;
                let table = <TableForSequential
                            listN={[...listN]}
                            nowIndexRes={p}
                            listRes={listRes2}
                            flag = {false}
                            />;
                temp_clue_table.push([clue, table]);
                p = (p + 1) % length;
            }
            listRes2[p] = array[i];
            let clue = `Loop: ${loop}`;
            let table = <TableForSequential
                            listN={[...listN]}
                            nowIndexRes={p}
                            listRes={listRes2}
                            flag = {true}
                            />;
            temp_clue_table.push([clue, table]);
            listRes = [...listRes2]
            tables.push(temp_clue_table);
        }
        let finalTable = <TableForSequential
                            listN={[...listN]}
                            nowIndexRes={-1}
                            listRes={listRes}
                            flag = {true}
                            />;
        let text = [];
        let index = -1;
        let mod = key % length;
        let hashTables = [];
        for (let i = 0; i < length; i++) {
            let temp = [];
            temp.push(`LOOP: ${i}`);
            let p = (mod + i) % length;
            let clue = "";
            if (i === 0) {
                clue += `mod = key(${key}) % ${length} = ${p}, `;
            } else {
                clue += `mod + ${i} = ${p}, `;
            }
            clue += `Array[${p}]=${listRes[p]}`;
            if (listRes[p] === key)
                clue += " is equal to ";
            else
                clue += " is not equal to ";
            clue += key;
            temp.push(clue);
            text.push(temp);
            hashTables.push(<TableForSequential
                            listN={[...listN]}
                            nowIndexRes={p}
                            listRes={listRes}
                            flag = {key === listRes[p]}
                            />);
            if (key === listRes[p]) {
                index = p;
                break;
            }
        }
        if (index !== -1) {
            allArray.splice(0,0,"Found: index is " + index);
        } else {
            allArray.splice(0, 0, "Not Found!");
        }
        this.setState({allArray, tables, start: true, finalTable, text, hashTables});
    }

    algoDisplay = () => {
        if (this.state.start) {
            if (this.state.algo === 0 || this.state.algo === 1) {
                let allArray = [...this.state.allArray];
                let [finalText] = allArray.splice(0, 1);
                let style;
                let mode = finalText[0];
                if (mode === 'F') {
                    style = {color: "green", backgroundColor: 'lightgreen'};
                } else {
                    style = {color: "red", backgroundColor: 'lightpink'};
                }
                let [array] = allArray.splice(0, 1);
                let tables = [...this.state.tables]
                return (
                    <div>
                        <div className="card m-2 mb-4">
                            <div className="card-body" style={style}>
                                {finalText}
                            </div>
                        </div>
                        {allArray.map((x, index)=>
                             <div className="card m-2">
                                <div className="card-body">
                                    <h4>{x[0]}</h4>
                                    <p style={{color: "lightgray"}}>{x[1]}</p>
                                    {tables[index]}
                                </div>
                            </div>
                        )}
                    </div>
                    );

            } else if (this.state.algo === 2) {
                let allArray = [...this.state.allArray];
                let hashTables = [...this.state.hashTables];
                let text = [...this.state.text];
                let [finalText] = allArray.splice(0, 1);
                let style;
                let mode = finalText[0];
                if (mode === 'F') {
                    style = {color: "green", backgroundColor: 'lightgreen'};
                } else {
                    style = {color: "red", backgroundColor: 'lightpink'};
                }
                allArray.splice(0, 1);
                let key = this.state.key;
                let tables = [...this.state.tables];
                return (
                    <div>
                        <div className="card m-2 mb-4">
                            <div className="card-body" style={style}>
                                {finalText}
                            </div>
                        </div>
                        <h2 className="m-4"><strong>Step 1: Insert values</strong></h2>
                        {allArray.map((x, index)=>
                             <div className="card m-2">
                                <div className="card-body">
                                    <h4>{x[0]}</h4>
                                    {tables[index].map((x)=>
                                    <div>
                                        <p style={{color: "lightgray"}}>{x[0]}</p>
                                        {x[1]}
                                    </div>
                                )}
                                </div>

                            </div>
                        )}
                        <h2 className="m-4"><strong>Step 2: Final HashTable</strong></h2>
                        <div className="card m-2">
                                <div className="card-body">
                                    {this.state.finalTable}
                                </div>
                            </div>

                        <h2 className="m-4"><strong>Step 3: Find key: {key}</strong></h2>
                        {text.map((x, index)=>
                             <div className="card m-2">
                                <div className="card-body">
                                    <h4>{x[0]}</h4>
                                    <p style={{color: "lightgray"}}>{x[1]}</p>
                                    {hashTables[index]}
                                </div>

                            </div>
                        )}
                    </div>
                    );
            }

        }
    }


    render = () =>  {
        return (
            <div >
                <Navbar
                    name={"Search"}
                />
                <Menu
                    onChange = {this.realOnReset}
                    onGenerate = {this.handleGenerate}
                    algo={this.state.algo}
                    setAlgo={this.setAlgo}
                    onStart={this.addNumber}
                    onReset={this.realOnReset}

                />

                {this.algoDisplay()}

            </div>
        );
    }
}

export default Search;